#include "StdAfx.h"
#include "HXTTSEngineMary.h"

HXTTSEngineMary::HXTTSEngineMary(void)
{
    m_host = "localhost";
	m_port = 59125;
	m_client = new MaryClient(m_host, m_port);
	m_avSyncAdjust = 0.000;
}

/*
HXTTSEngineMary::HXTTSEngineMary(int fps, double visemeModulator, double rate):HXTTSEngine(fps, visemeModulator, rate)
{
    m_host = "localhost";
	m_port = 59125;
	m_client = new MaryClient(m_host, m_port);
	m_avSyncAdjust = 0.000;
}
*/

HXTTSEngineMary::~HXTTSEngineMary(void)
{
	SAFE_DELETE(m_client);
}


HRESULT HXTTSEngineMary::GenerateSpeech(const wstring& text, string& audio, vector<vector<HXViseme>>& frames)
{
  	// mary variables
	string maryxml;
	int ok = MaryClient::ERROR_NONE;

	// set rate
	internal_SetRate(m_rate);

	bool isxml = false;
	if (!isxml)
	{
	

		// get rawmaryxml output from text
		m_client->setInputType("TEXT");
		m_client->setOutputType("RAWMARYXML"); 
		ok = m_client->process(WStringToString(text), maryxml);	
		if (ok != MaryClient::ERROR_NONE)
		{
			WNotify(NULL, L"ERROR Generating RAWMARYXML: %s", StringToWString(m_client->getErrorString(ok)).c_str());
			return ok;
		}

	}
	else
	{
		maryxml = WStringToString(text);
	}

	// get the Mary output for durations
	m_client->setInputType("RAWMARYXML");
	m_client->setOutputType("REALISED_DURATIONS"); // REALISED_DURATIONS, AUDIO, FESTIVAL_UTT
	ok = m_client->process(maryxml, audio);	
	if (ok != MaryClient::ERROR_NONE)
	{
		WNotify(NULL, L"ERROR Generating REALISED_DURATIONS: %s", StringToWString(m_client->getErrorString(ok)).c_str());
		return ok;
	}

	// map the phoneme durations to visemes
	parse(audio,frames);

	// get the Mary output for audio
	m_client->setOutputType("AUDIO");
	ok = m_client->process(maryxml, audio);		
	if (ok != MaryClient::ERROR_NONE)
	{
		WNotify(NULL, L"ERROR Generating AUDIO: %s", StringToWString(m_client->getErrorString(ok)).c_str());
		return ok;
	}

	return MaryClient::ERROR_NONE;
}

/**
* Parse input from Mary output. 
* Expecting the input to be of type REALISED_DURATIONS
*/
void HXTTSEngineMary::parse(const string& realised_durations, vector<vector<HXViseme>>& visemeSequence)
{
	string output;
	vector<string> phonelist;
	visemeSequence.clear();
	
	double previous = 0.0;
	double endpoint = 0.0;	

	// examine the input line by line
	tokenize(realised_durations, phonelist, "\n");

	for (vector<string>::size_type i = 0; i < phonelist.size(); i++)
	{		
		// parse each line, "<dur> 125 <pho>", where dur is actually the endpoint!?
		vector<string> phonebits;
		tokenize(phonelist.at(i), phonebits," ");

		// create viseme list
		if (phonebits.size() == 3)
		{
			endpoint = atof(phonebits[0].c_str());
			if (endpoint > m_avSyncAdjust)
			{
				endpoint -= m_avSyncAdjust;
				previous -= m_avSyncAdjust;
			}

			ToHXViseme(phonebits[2], endpoint, previous, visemeSequence);
			
			previous = endpoint;
		}

	}

}




void HXTTSEngineMary::tokenize(const string& str,
							vector<string>& tokens,
							const string& delimiters = " ")
{
	// Skip delimiters at beginning.
	string::size_type lastPos = str.find_first_not_of(delimiters, 0);
	// Find first "non-delimiter".
	string::size_type pos     = str.find_first_of(delimiters, lastPos);

	while (string::npos != pos || string::npos != lastPos)
	{
		// Found a token, add it to the vector.
		tokens.push_back(str.substr(lastPos, pos - lastPos));
		// Skip delimiters.  Note the "not_of"
		lastPos = str.find_first_not_of(delimiters, pos);
		// Find next "non-delimiter"
		pos = str.find_first_of(delimiters, lastPos);
	}
}

void HXTTSEngineMary::GetVoices(vector<string>& voices)
{
	if (m_client)
		m_client->getVoices(voices);
}


void HXTTSEngineMary::SetVoice(string& voice)
{
	if (m_client)
		m_client->setVoiceName(voice); // female, hmm-slt, male
	m_voiceName = voice;
}

void HXTTSEngineMary::SetEffect(string& effect, string& /*value*/)
{
	if (m_client)
		m_client->setEffects(effect); // e.g. F0Add(f0Add:50.0)+Rate(durScale:1.5), see the MaryServer or MaryClientGUI for more info
	m_effect = effect;
}


void HXTTSEngineMary::internal_SetRate(double /*rate*/)
{
	stringstream ss;
	int rpos = m_effect.find("Rate");
	double convertedRate = (-m_rate + 10) / 10;
	ss << "Rate(durScale:" << convertedRate << ")"; 

	if (m_effect.empty())
	{
		// no effect set
		
	}
	else if (rpos == string::npos)
	{
		// effect has been set but not the rate
		ss << "+"  << m_effect; 

	}
	else
	{
		// rate has been set before
		// find the ending ')'
		int bpos = rpos, ncount = 1, msize = (int)m_effect.size();		
		while (bpos < msize && m_effect[bpos++] != ')')  
		{ 
			ncount++; 
		}
		
		// remove old rate
		m_effect.erase(rpos,ncount);

		if (m_effect.empty())
		{		// add in new rate
			ss << m_effect;
		}
		else
		{
			ss << "+"  << m_effect; 
		}

	}

	m_client->setEffects(ss.str());
	m_effect = ss.str();

		//m_effect += "Rate(durScale:" +  ")";
}